/*		crypto.c  - Cryptographic routines for PGP.
		PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.
		
		(c) Copyright 1990-1996 by Philip Zimmermann.  All rights reserved.
		The author assumes no liability for damages resulting from the use
		of this software, even if the damage results from defects in this
		software.  No warranty is expressed or implied.
		
		Note that while most PGP source modules bear Philip Zimmermann's
		copyright notice, many of them have been revised or entirely written
		by contributors who frequently failed to put their names in their
		code.  Code that has been incorporated into PGP from other authors
		was either originally published in the public domain or is used with
		permission from the various authors.
		
		PGP is available for free to the public under certain restrictions.
		See the PGP User's Guide (included in the release package) for
		important information about licensing, patent restrictions on
		certain algorithms, trademarks, copyrights, and export controls.
		
		Modified: 12-Nov-92 HAJK
		Add FDL stuff for VAX/VMS local mode. 
		Reopen temporary files rather than create new version.
		
		Modified: 13-Dec-92 Derek Atkins <warlord@MIT.EDU)
		Added Multiple Recipients
		
		Modified 25-Feb-93 Colin Plumb
		Improved security of randseed.bin in strong_pseudorandom.
		Thoroughly revamped make_random_ideakey.
		
		Modified  6-May-93 Colin Plumb
		Changed to use the entry points in rsaglue.c.
		*/

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "mpilib.h"
#include "mpiio.h"
#include "random.h"
#include "memfile.h"
#include "md5.h"
#include "crypto.h"
#include "keymgmt.h"
#include "mdfile.h"
#include "language.h"
#include "pgp.h"
#include "exitpgp.h"
#include "zipup.h"
#include "rsaglue.h"
#include "idea.h"
#include "zunzip.h"
#include "fileio.h"
#ifdef MACTC5
#include "Macutil.h"
#include "Macutil2.h"
#include "Macutil3.h"
#include "Aestuff.h"
#include "MyBufferedStdio.h"
#include "ReplaceStdio.h"
#undef fopen
extern long GMTTimeshift(void);
extern unsigned char passhash[];
#endif

#define USE_LITERAL2

/* This variable stores the md5 hash of the current file, if it is
   available.  It is used in make_random_ideakey. */
static unsigned char md5buf[16];

/* This flag is set if the buffer above has been filled. */
char already_have_md5 = 0;

#ifdef	M_XENIX
long time();
#endif

/* Disk buffers, used here and in crypto.c */
byte textbuf[DISKBUFSIZE];

/*--------------------------------------------------------------------------*/

#ifdef MACTC5
void CToPascal(char *s)
{
		/* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */
		int i,j;
		j = string_length(s);
		for (i=j; i!=0; i--)
				s[i] = s[i-1];	/* move everything 1 byte to the right */
		s[0] = j;				/* Pascal length byte at beginning */
}		/* CToPascal */

void PascalToC( char *s )
{
		/* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */
		int i,j;
		for (i=0,j=((byte *) s)[0]; i<j; i++)
				s[i] = s[i+1];	/* move everything 1 byte to the left */
		s[i] = '\0';			/* append C string terminator */
}		/* PascalToC */

#else
void CToPascal(char *s)
{
		/* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */
		int i,j;
		j = string_length(s);
		for (i=j; i!=0; i--)
				s[i] = s[i-1];	/* move everything 1 byte to the right */
		s[0] = j;				/* Pascal length byte at beginning */
}		/* CToPascal */


void PascalToC( char *s )
{
		/* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */
		int i,j;
		for (i=0,j=((byte *) s)[0]; i<j; i++)
				s[i] = s[i+1];	/* move everything 1 byte to the left */
		s[i] = '\0';			/* append C string terminator */
}		/* PascalToC */

#endif
/* 
		Note:  On MSDOS, the time() function calculates GMT as the local
		system time plus a built-in timezone correction, which defaults to
		adding 7 hours (PDT) in the summer, or 8 hours (PST) in the winter,
		assuming the center of the universe is on the US west coast. Really--
		I'm not making this up!  The only way to change this is by setting 
		the MSDOS environmental variable TZ to reflect your local time zone,
		for example "set TZ=MST7MDT".  This means add 7 hours during standard
		time season, or 6 hours during daylight time season, and use MST and 
		MDT for the two names of the time zone.  If you live in a place like 
		Arizona with no daylight savings time, use "set TZ=MST7".  See the
		Microsoft C function tzset().  Just in case your local software
		environment is too weird to predict how to set environmental
		variables for this, PGP also uses its own TZFIX variable in
		config.pgp to optionally correct this problem further.	For example,
		set TZFIX=-1 in config.pgp if you live in Colorado and the TZ
		variable is undefined.
*/
word32 get_timestamp(byte *timestamp)
/*		Return current timestamp as a byte array in internal byteorder,
		and as a 32-bit word */
{
		word32 t;
		t = time(NULL);    /* returns seconds since GMT 00:00 1 Jan 1970 */

#ifdef _MSC_VER
#if (_MSC_VER == 700)
		/*	Under MSDOS and MSC 7.0, time() returns elapsed time since
		 *	GMT 00:00 31 Dec 1899, instead of Unix's base date of 1 Jan 1970.
		 *	So we must subtract 70 years worth of seconds to fix this.
		 *	6/19/92  rgb 
		*/
#define LEAP_DAYS		(((unsigned long)70L/4)+1)
#define CALENDAR_KLUDGE ((unsigned long)86400L * (((unsigned long)365L \
												   * 70L) + LEAP_DAYS))
		t -= CALENDAR_KLUDGE;
#endif
#endif
#ifdef MACTC5
		t -= GMTTimeshift();
#endif
		if (timestamp != NULL) {
				/* first, fill array in external byte order: */
				put_word32(t, timestamp);
				convert_byteorder(timestamp,4);
								/* convert to internal byteorder */
		}

		return t;		/* return 32-bit timestamp integer */
}		/* get_timestamp */


/*		Given timestamp as seconds elapsed since 1970 Jan 1 00:00:00,
		returns year (1970-2106), month (1-12), day (1-31).
		Not valid for dates after 2100 Feb 28 (no leap day that year).
		Also returns day of week (0-6) as functional return.
*/
static int date_ymd(word32 *tstamp, int *year, int *month, int *day)
{
		word32 days,y;
		int m,d,i;
		static short mdays[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
		days = (*tstamp)/(unsigned long)86400L; /* day 0 is 1970/1/1 */
		days -= 730L;	/* align days relative to 1st leap year, 1972 */
		y = ((days*4)/(unsigned long)1461L);	/* 1972 is year 0 */
		/* reduce to days elapsed since 1/1 last leap year: */
		d = (int) (days - ((y/4)*1461L));
		*year = (int)(y+1972);
		for (i=0; i<48; i++) {	/* count months 0-47 */
				m = i % 12;
				d -= mdays[m] + (i==1); /* i==1 is the only leap month */
				if (d < 0) {
						d += mdays[m] + (i==1);
						break;
				}
		}
		*month = m+1;
		*day = d+1;
		i = (int)((days-2) % (unsigned long)7L); /* compute day of week 0-6 */
		return i;		/* returns weekday 0-6; 0=Sunday, 6=Saturday */
}		/* date_ymd */


/*		Return date string, given pointer to 32-bit timestamp */
char *cdate(word32 *tstamp)
{
		int month,day,year;
		static char datebuf[20];
		if (*tstamp == 0)
				return "		  ";
		(void) date_ymd(tstamp,&year,&month,&day);
		sprintf(datebuf,"%4d/%02d/%02d", year, month, day);
		return (datebuf);
}		/* cdate */


/*		Return date and time string, given pointer to 32-bit timestamp */
char *ctdate(word32 *tstamp)
{
		int hours,minutes;
		static char tdatebuf[40];
		long seconds;
		seconds = (*tstamp) % (unsigned long)86400L;
								/* seconds past midnight today */
		minutes = (int)((seconds+30L) / 60L);
								/* round off to minutes past midnight */
		hours = minutes / 60;	/* hours past midnight */
		minutes = minutes % 60; /* minutes past the hour */
		sprintf(tdatebuf,"%s %02d:%02d GMT", cdate(tstamp), hours, minutes);
		return (tdatebuf);
}		/* ctdate */

/* Used to determine if nesting should be allowed. */
boolean legal_ctb(byte ctb)
{
		boolean legal;
		byte ctbtype;
		if (!is_ctb(ctb))				/* not even a bonafide CTB */
				return FALSE;
		/* Sure hope CTB internal bit definitions don't change... */
		ctbtype = (ctb & CTB_TYPE_MASK) >> 2;
		/* Only allow these CTB types to be nested... */
		legal = ( (ctbtype==CTB_PKE_TYPE)
				|| (ctbtype==CTB_SKE_TYPE)
				|| (ctbtype==CTB_CERT_SECKEY_TYPE)
				|| (ctbtype==CTB_CERT_PUBKEY_TYPE)
				|| (ctbtype==CTB_LITERAL_TYPE)
				|| (ctbtype==CTB_LITERAL2_TYPE)
				|| (ctbtype==CTB_COMPRESSED_TYPE)
				|| (ctbtype==CTB_CKE_TYPE)
				 );
		return legal;
}		/* legal_ctb */


/* Return nonzero if val doesn't match checkval, after printing a
 * warning.
 */
int
version_error(int val, int checkval)
{
		if (val != checkval) {
				sprintf(sErrorText, "Unsupported packet format - val=%d, checkval=%d", val, checkval);
				return 1;
		}
		return 0;
}

int
version_byte_error(int val)
{
		if (val != VERSION_BYTE_OLD && val != VERSION_BYTE_NEW) {
				sprintf(sErrorText, "Unsupported packet format - val=%d", val);
				return 1;
		}
		return 0;
}
				

/*-------------------------------------------------------------------------*/

/*
 * Make a random IDEA key.	Returns its length (the constant 16).
 * It also generates a random IV, which is placed in the key array
 * after the key proper, but is not counted in the length.
 * Reads IDEA random key and random number seed from file, cranks the
 * seed through the IDEA strong pseudorandom number generator,
 * and writes them back out.  This is used for generation of
 * cryptographically strong pseudorandom numbers.  This is mainly to
 * save the user the trouble of having to type in lengthy keyboard
 * sequences for generation of truly random numbers every time we want
 * to make a random session key.  This pseudorandom generator will only
 * work if the file containing the random seed exists and is not empty.
 * If this is not the case, it will be automatically created.
 *
 * The MD5 of the current file is used to "prewash" the random numbers,
 * to make it more difficult for an attacker to predict the output.
 *
 * The "skip" parameter says to skip that many bytes at the beginning,
 * used to generate a random IV only for conventional encryption.
 */
int make_random_ideakey(byte key[IDEAKEYSIZE+RAND_PREFIX_LENGTH],
							   int skip)
{
		int count;
		struct IdeaCfbContext cfb;
		byte buf[10];

		ideaCfbInit(&cfb, md5buf);
		burn(md5buf);

		if (cryptRandOpen(&cfb) < 0) {
				sprintf(sErrorText,LANG("Preparing random session key..."));

				 /* get some random key bits */
				trueRandAccum((IDEAKEYSIZE+RAND_PREFIX_LENGTH)*8);

				cryptRandInit(&cfb);
		}

		/*
		 * Generate a good random IDEA key and initial vector.	If we have
		 * no random bytes, the trueRandByte() part will be useless
		 */
		count = IDEAKEYSIZE+RAND_PREFIX_LENGTH;
		for (count = skip; count < IDEAKEYSIZE+RAND_PREFIX_LENGTH; count++)
				key[count] = cryptRandByte() ^ trueRandByte();

		/*
		 * Write out a new randseed.bin.  It is encrypted in precisely the
		 * same manner as the message itself, although the leading
		 * IV and check bytes are discarded.  This "postwash" is to
		 * ensure that it's easier to decrypt the message directly than to
		 * try to figure out what the key was by examining the entrails
		 * of the random number generator state in randseed.bin.
		 */
		ideaCfbInit(&cfb, key);
		memcpy(buf, key, 8);
		buf[8] = buf[6];
		buf[9] = buf[7];
		ideaCfbEncrypt(&cfb, buf, buf, 10);
		ideaCfbSync(&cfb);

		/* Save out the washed session key */
		cryptRandSave(&cfb);

		ideaCfbDestroy(&cfb);

		return IDEAKEYSIZE;
}


word32 getpastlength(byte ctb, MemFile *f)
{
	word32 length;
	unsigned int llength;	/* length of length */
	byte buf[8];

	fill0(buf,sizeof(buf));
	length = 0L;
	/* Use ctb length-of-length field... */
	llength = ctb_llength(ctb); 	/* either 1, 2, 4, or 8 */
	if (llength==8) /* 8 means no length field, assume huge length */
			return -1L; 	/* return huge length */

	/* now read in the actual length field... */
	if (mfread((byteptr) buf,llength,f) < llength)
			return (-2L); /* error -- read failure or premature eof */
	/* convert length from external byteorder... */
	if (llength==1)
			length = (word32) buf[0];
	if (llength==2)
			length = (word32) fetch_word16(buf);
	if (llength==4)
			length = fetch_word32(buf);
	return length;
} /* mfgetpastlength */

/* Write a CTB with the appropriate length field.  If big is true,
 * always use a four-byte length field.
 */
void write_ctb_len (MemFile *f, byte ctb_type, word32 length, boolean big)
{
		int 	llength, llenb;
		byte	buf[4];

		if (big || (length > 0xFFFFL)) {
				llength = 4;
				llenb = 2;
		} else if ((word16)length > 0xFF) {
				llength = 2;
				llenb = 1;
		} else {
				llength = 1;
				llenb = 0;
		}
		
		mfputc(CTB_BYTE(ctb_type, llenb), f);
		/* convert length to external byteorder... */
		if (llength==1)
				buf[0] = length;
		if (llength==2)
				put_word16((word16) length, buf);
		if (llength==4)
				put_word32(length, buf);
		mfwrite( buf, llength, f );
} /* write_ctb_len */

/*
 * Use IDEA in cipher feedback (CFB) mode to encrypt or decrypt a file. 
 * The encrypted material starts out with a 64-bit random prefix, which
 * serves as an encrypted random CFB initialization vector, and
 * following that is 16 bits of "key check" material, which is a
 * duplicate of the last 2 bytes of the random prefix.	Encrypted key
 * check bytes detect if correct IDEA key was used to decrypt ciphertext.
 */

int idea_file(byte *ideakey, boolean decryp, MemFile *f, MemFile *g, word32 lenfile)
{
		int count, status = 0;
		extern byte textbuf[DISKBUFSIZE];
		struct IdeaCfbContext cfb;
#define RAND_PREFIX_LENGTH 8

		/* init CFB key */
		ideaCfbInit(&cfb, ideakey);

		if (!decryp) {	/* encrypt-- insert key check bytes */
				/* There is a random prefix followed by 2 key check bytes */

				memcpy(textbuf, ideakey+IDEAKEYSIZE, RAND_PREFIX_LENGTH);
	/* key check bytes are simply duplicates of final 2 random bytes */
				textbuf[RAND_PREFIX_LENGTH] = textbuf[RAND_PREFIX_LENGTH-2];
				textbuf[RAND_PREFIX_LENGTH+1] = textbuf[RAND_PREFIX_LENGTH-1];

				ideaCfbEncrypt(&cfb, textbuf, textbuf, RAND_PREFIX_LENGTH+2);
				mfwrite(textbuf,RAND_PREFIX_LENGTH+2,g);
		} else { /* decrypt-- check for key check bytes */
				/* See if the redundancy is present after the random prefix */
				count = mfread(textbuf,RAND_PREFIX_LENGTH+2,f);
				lenfile -= count;
				if (count==(RAND_PREFIX_LENGTH+2)) {
						ideaCfbDecrypt(&cfb, textbuf, textbuf,
									   RAND_PREFIX_LENGTH+2);
						if ((textbuf[RAND_PREFIX_LENGTH] !=
							 textbuf[RAND_PREFIX_LENGTH-2])
								|| (textbuf[RAND_PREFIX_LENGTH+1] !=
									textbuf[RAND_PREFIX_LENGTH-1]))
						{
								status = -2;			/* bad key error */
						}
				} else	/* file too short for key check bytes */
						status = -3;			/* error of the weird kind */
		}

		ideaCfbSync(&cfb);

		/* read and write the whole file in CFB mode... */
		count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE;
		while (count && status == 0) {
				if ((count = mfread(textbuf,count,f)) <= 0) {
						status = -3;
						break;
				}
				lenfile -= count;
				if (decryp)
						ideaCfbDecrypt(&cfb, textbuf, textbuf, count);
				else
						ideaCfbEncrypt(&cfb, textbuf, textbuf, count);
				if (mfwrite(textbuf,count,g) != count)
						status = -3;
				count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE;
#ifdef MACTC5
				mac_poll_for_break();
#endif
		}

		ideaCfbDestroy(&cfb);	/* Clean up data structures */
		burn(textbuf);	/* burn sensitive data on stack */
		return status;	/* should always take normal return */
}		/* idea_file */


/* Checksum maintained as a running sum by read_mpi and write_mpi.
 * The checksum is maintained based on the plaintext values being
 * read and written.  To use it, store a 0 to it before doing a set
 * of read_mpi's or write_mpi's.  Then read it aftwerwards.
 */
word16	mpi_checksum;

/*
 * Read a mutiprecision integer from a file.
 * adjust_precision is TRUE iff we should call set_precision to the 
 * size of the number read in.
 * scrambled is TRUE iff field is encrypted (protects secret key fields).
 * Returns the bitcount of the number read in, or returns a negative
 * number if an error is detected.
 */
int read_mpi(unitptr r, MemFile *f, boolean adjust_precision,
			 struct IdeaCfbContext *cfb)
{
		byte buf[MAX_BYTE_PRECISION+2];
		unsigned int count;
		word16 bytecount,bitcount;

		mp_init(r,0);

		if ((count = mfread(buf,2,f)) < 2)
				return (-1); /* error -- read failure or premature eof */

		bitcount = fetch_word16(buf);
		if (bits2units(bitcount) > global_precision)
				return -1;		/* error -- possible corrupted bitcount */

		bytecount = bits2bytes(bitcount);

		count = mfread(buf+2,bytecount,f);
		if (count < bytecount)
				return -1;		/* error -- premature eof */

		if (cfb) {		/* decrypt the field */
				ideaCfbSync(cfb);
				ideaCfbDecrypt(cfb, buf+2, buf+2, bytecount);
		}

		/* Update running checksum, in case anyone cares... */
		mpi_checksum += checksum (buf, bytecount+2);

		/*		We assume that the bitcount prefix we read is an exact
				bitcount, not rounded up to the next byte boundary.
				Otherwise we would have to call mpi2reg, then call
				countbits, then call set_precision, then recall mpi2reg
				again.
		*/
		if (adjust_precision && bytecount) {
				/* set the precision to that specified by the number read. */
				if (bitcount > MAX_BIT_PRECISION-SLOP_BITS)
						return -1;
				set_precision(bits2units(bitcount+SLOP_BITS));
				/* Now that precision is optimally set, call mpi2reg */
		}

		if (mpi2reg(r,buf) == -1)		/* convert to internal format */
				return -1;
		burn(buf);		/* burn sensitive data on stack */
		return (bitcount);
}		/* read_mpi */

int mfread_mpi(unitptr r, MemFile *f, boolean adjust_precision,
			 struct IdeaCfbContext *cfb)
{
		byte buf[MAX_BYTE_PRECISION+2];
		unsigned int count;
		word16 bytecount,bitcount;

		mp_init(r,0);

		if ((count = mfread(buf,2,f)) < 2)
				return (-1); /* error -- read failure or premature eof */

		bitcount = fetch_word16(buf);
		if (bits2units(bitcount) > global_precision)
				return -1;		/* error -- possible corrupted bitcount */

		bytecount = bits2bytes(bitcount);

		count = mfread(buf+2,bytecount,f);
		if (count < bytecount)
				return -1;		/* error -- premature eof */

		if (cfb) {		/* decrypt the field */
				ideaCfbSync(cfb);
				ideaCfbDecrypt(cfb, buf+2, buf+2, bytecount);
		}

		/* Update running checksum, in case anyone cares... */
		mpi_checksum += checksum (buf, bytecount+2);

		/*		We assume that the bitcount prefix we read is an exact
				bitcount, not rounded up to the next byte boundary.
				Otherwise we would have to call mpi2reg, then call
				countbits, then call set_precision, then recall mpi2reg
				again.
		*/
		if (adjust_precision && bytecount) {
				/* set the precision to that specified by the number read. */
				if (bitcount > MAX_BIT_PRECISION-SLOP_BITS)
						return -1;
				set_precision(bits2units(bitcount+SLOP_BITS));
				/* Now that precision is optimally set, call mpi2reg */
		}

		if (mpi2reg(r,buf) == -1)		/* convert to internal format */
				return -1;
		burn(buf);		/* burn sensitive data on stack */
		return (bitcount);
}		/* mfread_mpi */



/*
 * Write a multiprecision integer to a file.
 * scrambled is TRUE iff we should scramble field on the way out,
 * which is used to protect secret key fields.
 */
void write_mpi(unitptr n, MemFile *f, struct IdeaCfbContext *cfb)
{
		byte buf[MAX_BYTE_PRECISION+2];
		short bytecount;
		bytecount = reg2mpi(buf,n);
		mpi_checksum += checksum (buf, bytecount+2);
		if (cfb) { /* encrypt the field, skipping over the bitcount */
				ideaCfbSync(cfb);
				ideaCfbEncrypt(cfb, buf+2, buf+2, bytecount);
		}
		mfwrite(buf,bytecount+2,f); 
		burn(buf);		/* burn sensitive data on stack */
}		/* write_mpi */

void mfwrite_mpi(unitptr n, MemFile *f, struct IdeaCfbContext *cfb)
{
		byte buf[MAX_BYTE_PRECISION+2];
		short bytecount;
		bytecount = reg2mpi(buf,n);
		mpi_checksum += checksum (buf, bytecount+2);
		if (cfb) { /* encrypt the field, skipping over the bitcount */
				ideaCfbSync(cfb);
				ideaCfbEncrypt(cfb, buf+2, buf+2, bytecount);
		}
		mfwrite(buf,bytecount+2,f); 
		burn(buf);		/* burn sensitive data on stack */
}		/* write_mpi */

/*======================================================================*/

/*		Reads the first count bytes from infile into header. */
int get_header_info_from_mfile(MemFile *infile,  byte *header, int count)
{
	fill0(header,count);
	/* open file f for read, in binary (not text) mode...*/
	/* read Cipher Type Byte, and maybe more */
	return mfread(header,count,infile);	/* normal return */
}	/* get_header_info_from_file */


/* System clock must be broken if it isn't past this date: */
#define REASONABLE_DATE ((unsigned long) 0x27804180L)  /* 91 Jan 01 00:00:00 */


/*		Constructs a signed message digest in a signature certificate.
		Returns total certificate length in bytes, or returns negative
		error status.
*/

int make_signature_certificate(byte *certificate, struct MD5Context *MD,
   byte class, long keySerial, unitptr e, unitptr d, unitptr p, unitptr q, unitptr u, unitptr n)
{
		byte inbuf[MAX_BYTE_PRECISION], outbuf[MAX_BYTE_PRECISION+2];
		byte iv[4];
		int i, j, certificate_length, blocksize,bytecount;
		word16 ske_length;
		word32 tstamp; byte *timestamp = (byte *) &tstamp;
		byte val;
		int mdlen = 9;	/* length of class plus keySerial plus timestamp, for adding to MD */

/*		Note that RSA key must be at least big enough to encipher a
		complete message digest packet in a single RSA block. */

		blocksize = countbytes(n)-1;	/* size of a plaintext block */
		if (blocksize < 31) {
				sprintf(sErrorText,
"\n\007Error: RSA key length must be at least 256 bits.\n");
				return -1;
		}

		memcpy(iv, &keySerial, 4);
		convert_byteorder(iv, 4);	/* convert to external form */

		get_timestamp(timestamp); /* Timestamp when signature was
									 made */
		if (tstamp < REASONABLE_DATE) {
				/* complain about bad time/date setting */
				sprintf(sErrorText,
LANG("\n\007Error: System clock/calendar is set wrong.\n"));
				return -1;
		}
		convert_byteorder(timestamp,4); /* convert to external form */

		/* Finish off message digest calculation with this information */
		MD_addbuffer (MD, &class, 1, 0);
		MD_addbuffer (MD, iv, 4, 0);
		MD_addbuffer (MD, timestamp, 4, md5buf);
/* We wrote the digest to a static variable because we want to keep it around
   for random number generation later.	 Also make a note of that fact. */
		already_have_md5 = 1;

		/* do RSA signature calculation: */
		i = rsa_private_encrypt((unitptr)outbuf, md5buf, sizeof(md5buf),
								e, d, p, q, u, n);
		if (i < 0) {
				if (i == -4) {
						sprintf(sErrorText,
   "\n\007Error: RSA key length must be at least 256 bits.\n");
				} else if (i == -3) {
						sprintf(sErrorText,
"\a\nError: key is too large.  RSA keys may be no longer than 1024 bits\
,\ndue to limitations imposed by software provided by RSADSI.\n");
				} else {
						sprintf(sErrorText,"\a\nUnexpected error %d signing\n", i);
				}
				return i;
		}

		/* bytecount does not include the 2 prefix bytes */
		bytecount = reg2mpi(outbuf,(unitptr)outbuf); /* convert to external
														format */
		/*		outbuf now contains a message digest in external byteorder 
				form.  Now make a complete signature certificate from this.
				(Note that the first two bytes of md5buf are used below as
				part of the certificate.)
		*/

		certificate_length = 0;

   /* SKE is Secret Key Encryption (signed).  Append CTB for signed msg. */
		certificate[certificate_length++] = CTB_SKE;

   /* SKE packet length does not include itself or CTB prefix: */
		ske_length = 1 + 1		  /* version and mdlen byte */
		  + mdlen				  /* class, keySerial, timestamp and validation period */ 
			+ 1 + 1 /* keySerial and 2 algorithm bytes */
			  + 2 + bytecount+2;  /* 2 MD bytes and RSA MPI w/bitcount */
		put_word16((word16) ske_length, certificate+certificate_length);
		certificate_length+=2;	/* advance past word */

		certificate[certificate_length++] = version_byte;

		/* Begin fields that are included in MD calculation... */

		certificate[certificate_length++] =  mdlen; /* mdlen is length
													   of MD-extras */

		certificate[certificate_length++] =  class & 0xff;

		/* Now append keySerial... */
		for (i=0; i < 4; i++)
			certificate[certificate_length++] = iv[i];

		/* timestamp already in external format */
		for (j=0; j<SIZEOF_TIMESTAMP; j++)
				certificate[certificate_length++] =  timestamp[j];
 
		/* ...end of fields that are included in MD calculation */

		certificate[certificate_length++] = RSA_ALGORITHM_BYTE;
		certificate[certificate_length++] = MD5_ALGORITHM_BYTE;

		/* Now append first two bytes of message digest */
		certificate[certificate_length++] = md5buf[0];
		certificate[certificate_length++] = md5buf[1];;

		/* Now append the RSA-signed message digest packet: */
		for (i=0; i<bytecount+2; i++)
				certificate[certificate_length++] = outbuf[i];

		burn(inbuf);	/* burn sensitive data on stack */
		burn(outbuf);	/* burn sensitive data on stack */

		return certificate_length; /* return length of certificate in bytes */

}		/* make_signature_certificate */

#ifdef VMS
/*
 * Local mode VMS, we write out the word VMS to say who owns the data then
 * we follow that with the file's FDL generated earlier by fdl_generate().
 * This FDL is preceded by a sixteen bit size. The file follows.
 */
void write_litlocal(FILE *g, char *fdl, short fdl_len)
{
	fputc('\0', g); /* Kludge for null literal file name (supplied by FDL) */
	fputs("VMS ", g);
	fwrite(&fdl_len, 2, 1, g); /* Byte order *not* important,
								  only VMS reads this!*/
	fwrite(fdl, 1, fdl_len, g);
}
#endif /* VMS */

/*======================================================================*/
int squish_and_idea_file(byte *ideakey, MemFile *f, MemFile *g, boolean attempt_compression)
{
		MemFile *t;
		word32 fpos, fpos0;

		/*
		**	If the caller specified that we should attempt compression, we
		**	create a temporary file 't' and compress our input file 'f' into
		**	't'.  Ideally, we would see if we get a good compression ratio 
		**	and if we did, then use file 't' for input and write a 
		**	CTB_COMPRESSED prefix.	But in this implementation we just always
		**	use the compressed output, even if it didn't compress well.
		*/

		mfrewind( f );

		if (!attempt_compression)
				t = f;	/* skip compression attempt */
		else	/* attempt compression-- get a tempfile */ 
				if ((t = mfopen_w(1024)) == NULL)
				  /* error: no tempfile */
						t = f;	/* skip compression attempt */
				else	/* attempt compression */ 
				{
						/* We don't put a length field on CTB_COMPRESSED yet */
						
						mfputc(CTB_COMPRESSED, t); /* write CTB_COMPRESSED */
								/* No CTB packet length specified
								   means indefinite length. */
						mfputc(ZIP2_ALGORITHM_BYTE, t); /* write ZIP algorithm
														 byte */

						/* Compression the file */
						zipup(f, t);
						if (mfwrite_error(t)) {
								mfclose(t);
								return -1;
						}
						mfrewind( t );
				}

		/*		Now write out file thru IDEA cipher... */

		/* Write CTB prefix, leave 4 bytes for later length */
		fpos0 = mftell(g);
		write_ctb_len (g, CTB_CKE_TYPE, 0L, TRUE);
		fpos = mftell(g) - fpos0;

		idea_file( ideakey, ENCRYPT_IT, t, g, mfsize(t) );

		/* Now re-write CTB prefix, this time with length */
		mfseek(g,fpos0,SEEK_SET);
		write_ctb_len (g, CTB_CKE_TYPE, mfsize(g)-fpos, TRUE);

		if (t != f) {
				mfclose( t );  /* close and remove the temporary file */
		}

		return 0;		/* normal return */

}		/* squish_and_idea_file */

#define NOECHO1 1		/* Disable password from being displayed on screen */
#define NOECHO2 2		/* Disable password from being displayed on screen */

void InitMD5(MemFile *f)
{
	if (!already_have_md5) {
		/* Obtain some random bits from the input file */
		struct MD5Context MD;

		MD5Init(&MD);
		MDfile0_len(&MD, f, 4096); /* Ignore errors - what could be
									  done? */
		MD5Final(md5buf, &MD);
		already_have_md5 = 1;

		mfseek(f, 0, SEEK_SET); /* Get back to the beginning for
								  encryption */
	}
}

#undef LENGTH_FIELD
#undef LIT_TYPE

/*======================================================================*/

int decompress_file(MemFile *f, MemFile *g)
{
		byte ctb;

		/* open file f for read, in binary (not text) mode...*/
		mfrewind(f);
		mfread(&ctb,1,f);		/* read and skip over Cipher Type Byte */
		if (!is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) {
				/* Shouldn't get here, or why were we called to begin with? */
				sprintf(sErrorText,"\007\nBad or missing CTB_COMPRESSED byte.\n");
				return -1;		/* Abandon ship! */
		}

		getpastlength(ctb, f); /* read packet length */
		/* The packet length is ignored.  Assume it's huge. */

		mfread(&ctb,1,f);		/* read and skip over compression
								   algorithm byte */
		if (ctb != ZIP2_ALGORITHM_BYTE) {
				/* We only know how to uncompress deflate-compressed data.	We
				   may hit imploded or Lharc'ed data but treat it as an error
				   just the same */
				sprintf(sErrorText, "Unrecognized compression algorithm");
				return -1;		/* Abandon ship! */
		}

		/*		open file g for write, in binary (not text) mode... */
		if (unzip( f, g )) {
				sprintf(sErrorText,
LANG("\n\007Decompression error.  Probable corrupted input.\n"));
				return -1;
		}

		if (mfwrite_error(g))
				return -1;
		return 1;
} /* decompress_mfile */
